using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace theRift
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class theRift : Microsoft.Xna.Framework.Game
    {
        protected GraphicsDeviceManager graphics;
        protected SpriteBatch spriteBatch;
        protected SpriteFont smallFont, bigFont;
        protected GameScene activeScene;
        protected GameScene parentScene;
        protected MenuScene menuScene;
        protected WorldScene worldScene;
        protected DialogScene dialogScene;
        protected KeyboardState oldKeyboardState;
        protected InputState inputState;
        public Random rnd { get; protected set; }

        protected CameraManager cameraManager;
        protected LightManager lightManager;

        //protected Settings settings;
        protected GUI gui;

        public theRift()
        {
            graphics = new GraphicsDeviceManager(this);

            Content.RootDirectory = "Content";

#if !DEBUG
            graphics.IsFullScreen = true;
#endif

            graphics.PreferredBackBufferWidth = Settings.PreferredBackBufferWidth;
            graphics.PreferredBackBufferHeight = Settings.PreferredBackBufferHeight;

            graphics.MinimumVertexShaderProfile = ShaderProfile.VS_2_0;
            graphics.MinimumPixelShaderProfile = ShaderProfile.PS_2_A;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {         
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            oldKeyboardState = Keyboard.GetState();

            inputState = new InputState(this);
            Components.Add(inputState);
            Services.AddService(typeof(InputState), inputState);

            rnd = new Random();

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
            Services.AddService(typeof(SpriteBatch), spriteBatch);

            cameraManager = new CameraManager(this);
            Components.Add(cameraManager);
            Services.AddService(typeof(CameraManager), cameraManager);

            //settings = new Settings();
            //Services.AddService(typeof(Settings), settings);

            gui = new GUI(this);
            Services.AddService(typeof(GUI), gui);

            lightManager = new LightManager();
            lightManager.AmbientLightColor = new Vector3(0.3f, 0.3f, 0.3f);
            Services.AddService(typeof(LightManager), lightManager);

            // Load in fonts
            smallFont = Content.Load<SpriteFont>(@"Fonts/smallFont");
            bigFont = Content.Load<SpriteFont>(@"Fonts/bigFont");
            
            // Create the menu scene
            menuScene = new MenuScene(this, smallFont, bigFont);
            Components.Add(menuScene);
            menuScene.Show();
            activeScene = menuScene;

            // Create the world scene
            worldScene = new WorldScene(this);
            Components.Add(worldScene);

            dialogScene = new DialogScene(this, smallFont);
            dialogScene.AddPage("Hello, it's me, the dialogue! Enter to continue.");
            dialogScene.AddPage("I will be talking to you for a bit now.");
            dialogScene.AddPage("As you might have noticed I'm not the most attractive.");
            dialogScene.AddPage("But at least I'm functional right?");
            dialogScene.AddPage("If requested, I can even present you with text worthy of two lines. Now that's something right?");
            dialogScene.AddPage("Be real nice to me however and I can even beat that and go up to the massive number of 3 lines! I bet you didn't see that coming, did you?");
            dialogScene.AddPage("I'll be leaving you alone now, goodbye.");
            Components.Add(dialogScene);
           
            ThirdPersonCamera camera = new ThirdPersonCamera();
            camera.DesiredPositionOffset = new Vector3(0.0f, 10.0f, 3.0f);
            camera.LookAtOffset = new Vector3(0.0f, 1.0f, 0.0f);
            camera.FOV = MathHelper.PiOver4;
            camera.NearPlane = 1.0f;
            camera.FarPlane = 10000.0f;
            cameraManager.Add("camera", camera);

            base.LoadContent();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (activeScene == menuScene)
                MenuInput();

            if (activeScene == worldScene)
                WorldInput();

            if (activeScene == dialogScene)
                DialogInput();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            base.Draw(gameTime);
        }

        private void LoadScene(GameScene scene, bool hide)
        {
            if (hide)
                activeScene.Hide();
            else
                parentScene = activeScene;

            activeScene = scene;
            activeScene.Show();
        }

        private bool InputEnter()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            bool result = (oldKeyboardState.IsKeyDown(Keys.Enter) && (keyboardState.IsKeyUp(Keys.Enter)));

            oldKeyboardState = keyboardState;

            return result;
        }

        private void MenuInput()
        {
            if (InputEnter())
            {
                switch (menuScene.Menu.SelectedIndex)
                {
                    case 0:
                        LoadScene(worldScene, true);
                        break;
                    case 1:
                        break;
                    case 2:
                        break;
                    case 3:
                        Exit();
                        break;
                }
            }
        }

        private void WorldInput()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.P))
            {
                LoadScene(dialogScene, false);
            }

            if (keyboardState.IsKeyDown(Keys.H))
                worldScene.Player.CurHealth++;
            if (keyboardState.IsKeyDown(Keys.J))
                worldScene.Player.CurHealth--;
        }

        private void DialogInput()
        {
            if (InputEnter())
            {
                dialogScene.CurPage++;

                if (dialogScene.CurPage == dialogScene.PageCount)
                {
                    dialogScene.DialogEnd();
                    LoadScene(worldScene, true);
                }
            }
        }

        static void Main(string[] args)
        {
            using (theRift game = new theRift())
            {
                game.Run();
            }
        }
    }
}